#ifndef SEARCH_H
#define SEARCH_H
#include <QCoreApplication>
#include <qDebug>
#include <iostream>
#include "contemplation.h"
#include "objectid.h"
#include "relationshipinfo.h"
#include "baseobject.h"
#include "characteristic.h"

class Search
{
    private:
    Contemplation cont;
    QList<RelationshipInfo> shortRelInfo;

    public:
    Search()
    {
        cout << "Creating a search class" << endl;
    }


    //***********************************************************************************************************************
    // This method adds a new relationship to the relationship table. If the relationship table does not exist, it creates it.
    bool addRelationshipInfo(QString relation, ObjectClass subject, ObjectClass object)
    {
        //cout << "adding  a relationship" << endl;
        RelationshipInfo relInfo;
        bool found;

        // check local short list for information
        for(int i = 0; i < shortRelInfo.size(); i++)
        {
            if(shortRelInfo[i].name == relation)
            {
                relInfo = shortRelInfo[i];
                found = true;
                break;
            }
        }

        // if the information wasn't in the short list, we check the database
        if(!found)
        {
            relInfo = cont.getRelTableInfo(relation);
        }

        // if the relationship wasn't already in the database, we add it to the database and as a word
        if(relInfo.relationshipID == -1)
        {
            relInfo = cont.MakeRelationshipTable(relation);
            BaseObject bo = addNewWord("abstract", relation);
            int boTable = cont.tableNumber("words");
            QString table = "rel_isarelationship";
            int newID = cont.AddTuple(table,"subject_table", boTable);
            cont.AddValue(table, "subject_id", bo.id, newID);
            cont.AddValue(table, "object_table", boTable, newID);
            cont.AddValue(table, "object_id", 1, newID);

        }

        // check if there is still no relationship, throw and error and return
        if(relInfo.relationshipID == -1)
        {
            qDebug() << "Error adding new relationship " << relation;
            return false;
        }

        // we now have a relationship, but if it wasn't on the short list, we add it
        if(!found)
        {
            shortRelInfo.push_back(relInfo);
        }

        // now we check to be certain the relationship doesn't already exist, and return false if it does
        if(cont.relInTable(relInfo.tableName, subject.table, subject.id, object.table, object.id))
        {
            cout << endl << "The specific relationship was already in the table" << endl;
            return false;
        }


        // now we have an existing relationship, we add the objects into the table
        int newId = cont.AddTuple(relInfo.tableName, "subject_table", subject.table);
        cont.AddValue(relInfo.tableName, "subject_id", subject.id, newId);
        cont.AddValue(relInfo.tableName, "object_table", object.table, newId);
        cont.AddValue(relInfo.tableName, "object_id", object.id, newId);

        // And, finally, we add this new information into the inductive tally table for all the words that the subject connects with
        // get list of all the higher up connections for subject

        QList<BaseObject> isas = cont.UpWordTrees(subject);
        //cout << "we have " << isas.size() << " words associated with this object" << endl;
        for (int i = 0; i < isas.size(); i++)
        {
            cont.increaseTally(relation, isas[i], object);
        }

        //cont.tallyRelations(bos, subject);
//     addRelationshipInfo(QString relation, ObjectClass subject, ObjectClass object)new raven object, wings

        return true;
    }

    //************************8
    // returns list of words that an object belongs to
    QList<BaseObject> UpWordTrees(ObjectID id)
    {
        QList<BaseObject> isas = cont.UpWordTrees(id);
        qDebug() << "in search the size is " << isas.size();
        return isas;
    }

    //**********************************************************************************************************************88
    // This method adds a new object to the words database, including the text if available
    BaseObject addNewWord(int pos, int text = NULL)
    {
        BaseObject bo;
        bo = cont.addNewWord(pos, text);
        return bo;
    }

    BaseObject addNewWord(int pos, QString text)
    {
        int textId;
        textId = cont.getTextID(text);
        // if the word was not found, add it
        if(textId == -1)
        {
            textId = cont.AddTuple("text", "spelling", text);
        }
        BaseObject bo;
        bo = addNewWord(pos, textId);
        return bo;
    }

    BaseObject addNewWord(QString pos, QString text)
    {
        int posId;
        posId = cont.getPosID(pos);
        BaseObject bo;
        // check if the pos was found
        if(posId == -1)
        {
            cout << "Error - that part of speech was not found" << endl;
            return bo;
        }
        bo = addNewWord(posId, text);
        return bo;
    }

    ObjectID addNewObject(QList<BaseObject> bos)
    {
        ObjectID id = cont.makeNewObject();

        for(int i = 0; i < bos.size(); i++)
        {
            relateObjectToWord(bos[i], id);
        }

        return id;
    }

    ObjectID addNewObject(BaseObject bos)
    {
        ObjectID id = cont.makeNewObject();
        relateObjectToWord(bos, id);

        return id;
    }

    ObjectID addNewObject()
    {
        ObjectID id;
        id = cont.makeNewObject();
        return id;
    }

    bool relateObjectToWord(BaseObject bos, ObjectID id)
    {
        int wordTable = cont.tableNumber("words");
        QString table = "rel_object_to_word";

            int newEntryId = cont.AddTuple(table, "object_table", wordTable);
            cont.AddValue(table, "object_id", bos.id, newEntryId);
            cont.AddValue(table, "subject_table", id.table, newEntryId);
            cont.AddValue(table, "subject_id", id.id, newEntryId);

            // go through every relationship in id and add that relationship info alongside bos info in rel_inductive_tally
            cont.tallyRelations(bos, id);

            //increment the word count
            cont.incrementWordLeaves(bos.id);

        return true;
    }

    //***************************************************************************************************************************
    // This method returns a list of all the relationship table names
    QList<QString> relTableNames()
    {
        QList<QString> relationships;
        return cont.relTableNames();
    }

    //***************************************************************************************************************************
    // This method returns the table number for a given table name
    int tableNumber(QString table)
    {
        return cont.tableNumber(table);
    }

    //**************************************************************************************************************************
    QList<int> findNouns()
    {
        QList<int> found = cont.find();
        return found;
    }

    //***********************************************************************************************************************

    void showRelationships()
    {
        cont.displayTable("RelationshipTrees");
    }

    //***********************************************************************************************************************
    // This method returns a list of texts for a given word object
    QList<QString> getTextForWord(int word)
    {
        QList<QString> texts;
        texts = cont.getTextForWord(word);

        return texts;
    }

    //***********************************************************************************************************************
    // This method displays the table given
    void displayTable(QString table)
    {
        cont.displayTable(table);
    }

    //***********************************************************************************************************************
    // This method displays the Tables table
    void showTables()
    {
        cont.showTables();
    }

    //***********************************************************************************************************************
    // this method clears the entire database and starts over
    void clearDatabase()
    {
        cont.clearDatabase();
    }

    //***********************************************************************************************************************
    // this method checks the likelihood that a baseobject will have a characteristic
    QString wordCharacteristic(BaseObject bo, QString table, ObjectClass characteristic)
    {
        QString response;
        QList<Characteristic> characters;
        int tableNum;
        int numFor;
        int numAgainst;
        int totalNumber;
        int calculated;
        bool discovered = false;


        // get the table number
        tableNum = cont.tableNumber(table);

        // get the total number of objects in the base object
        totalNumber = cont.getIntValueFromTable("words", "total_leaves", "id", bo.id);
        if(totalNumber < 5)
        {
            response = "I can't really be sure, I've only seen a few";
            return response;
        }

        // get a list of characteristics for the base object
        characters = cont.getCharacteristic(bo);

        cout << endl << "the size of the characteristics list is " << characters.size() << endl;

        // look for the objectID and table number in the list
        for(int i = 0; i < characters.size(); i++)
        {
            if(characters[i].object_id == characteristic.id && characteristic.table == characters[i].object_table && characters[i].rel_table == tableNum)
            {
                discovered = true;
                numFor = characters[i].num_for;
                if(numFor < 0)
                {
                    response = "Yes, absolutely, by definition";
                }
                numAgainst = characters[i].num_against;
                if(numAgainst == NULL)
                    numAgainst = 0;
                if(numFor == NULL)
                    numFor = 0;
                calculated = 100*numFor/totalNumber;

                qDebug() << "num_for is " << numFor << " Total count is " << totalNumber << " and calculated is " << calculated;


                if(calculated == 100)
                {
                    response = "It is extremely likely";
                }
                else if(calculated > 90)
                {
                    response = "Very likely, but not necessarily";
                }
                else if(calculated > 80)
                {
                    response = "Probably, but you can not be certain";
                }
                else if(calculated > 70)
                {
                    response = "Probably, sorta";
                }
                else if(calculated > 60)
                {
                    response = "The odds are in your favor";
                }
                else if(calculated > 50)
                {
                    response = "It is more likely than not";
                }
                else if(calculated > 40)
                {
                    response = "Probably not";
                }
                else
                {
                    response = "I would not count on it";
                }
            }
        }

        if(!discovered)
        {
            response = "I have no idea - there's not enough information, but probably not, no.";
        }

        // if not found, return idk
        // if found, calculate the likelihood based on the number for and total number tallied, return response

        return response;
    }




};

#endif // SEARCH_H
